#[cfg(test)]
pub mod test_error {
    /*
    Rust 错误处理概述
    - Rust的可靠性:错误处理
        - 大部分情况下:在编译时提示错误，并处理
    - 错误的分类: 
        - 可恢复: 例如文件未找到，可再次尝试 
        - 不可恢复: bug，例如访问的索引超出范围
    - Rust 没有类似异常的机制
        - 可恢复错误:Result<T,E> 
        - 不可恢复:panic!宏
    
    不可恢复的错误与panic!
    - 当panic!宏执行:
        - 你的程序会打印一个错误信息
        - 展开(unwind)、清理调用栈(Stack) 
        - 退出程序
    */

    use std::{error::Error, fs::File, io::{self, ErrorKind, Read}};

    /* 为应对 panic，展开或中止(abort)调用栈
·默认情况下，当panic 发生: 
    -程序展开调用栈(工作量大)
        ·Rust沿着调用栈往回走
        ·清理每个遇到的函数中的数据 
    - 或立即中止调用栈:
        ·不进行清理，直接停止程序
        ·内存需要 OS进行清理
·想让二进制文件更小，把设置从“展开”改为“中止”: 
    -在Cargo.toml 中适当的profile部分设置: .panic = 'abort’

     */
    #[test]
    fn err01() {
        panic!(">>> abort");
    }
    #[test]
    fn err02() {
        let v = vec![1, 2, 3];
        v[99];
    }

    /* 使用 panic!产生的回溯信息
·panic! 可能出现在: 
    - 我们写的代码中
    - 我们所依赖的代码中
·可通过调用panic!的函数的回溯信息来定位引起问题的代码
·通过设置环境变量RUST_BACKTRACE可得到回溯信息
·为了获取带有调试信息的回溯，必须启用调试符号(不带--release)
     */

    /*
Result 枚举
enum Result<T,E>{
    Ok(T), 
    Err(E)，
}
·T:操作成功情况下，Ok变体里返回的数据的类型
·E:操作失败情况下，Err变体里返回的错误的类型(例子)

处理 Result 的一种方式:match 表达式
·和 Option 枚举一样，Result及其变体也是由prelude 带入作用域
     */
    #[test]
    fn err04() {
        let f = File::open("a.txt");
        let f = match f {
            Ok(get_file) => get_file,
            Err(err) => {
                panic!("-------------->>> err {:?}", err);
            }
        };
    }

    /*
        匹配不同的错误·(例子)
·上例中使用了很多 match...·match 很有用，但是很原始
·闭包(closure)。Result<T,E>有很多方法: 
    - 它们接收闭包作为参数 
    - 使用 match 实现
    - 使用这些方法会让代码更简洁
     */

    #[test]
    fn err05() {
        let f = File::open("target/a.txt");
        let f = match f {
            Ok(get_file) => get_file,
            Err(err) => match err.kind() {
                ErrorKind::NotFound => match File::create("target/a.txt") {
                    Ok(fc) => fc,
                    Err(e) => panic!("error create file: {:?}", e),
                },
                other_err => panic!("other error: {:?}", other_err),
            },
        };

        // ||{} 闭包
        let f1 = File::open("target/b.txt").unwrap_or_else(|err| {            
            if err.kind() == ErrorKind::NotFound {
                File::create("target/b.txt").unwrap_or_else(| err1 | {
                    panic!("error create file: {:?}", err1);
                })
            } else {
                panic!("other error: {:?}", err);
            }
        });
    }

    /*
    unwrap
.unwrap: match表达式的一个快捷方法: 
- 如果 Result结果是Ok，返回 Ok里面的值 
- 如果 Result结果是Err，调用panic!宏
     */
    #[test]
    fn err06() {
        let f1 = File::open("target/c.txt").unwrap();
    }

    #[test]
    fn err07() {
        let f2 = File::open("target/c.txt").expect("无法打开文件");
    }

    /*
    传播错误
·在函数出处理错误
·将错误返回给调用者
 */

 /*
? 运算符
·?运算符:传播错误的一种快捷方式·(例子)
·如果 Result是Ok:Ok中的值就是表达式的结果，然后继续执行程序
·如果 Result是Err: Err 就是整个函数的返回值，就像使用了return
 */
    #[test]
    fn err08() {
        let r = read_file();
    }

    fn read_file() -> Result<String, io::Error> {
        let mut f = File::open("Cargo.toml")?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        Ok(s)
    }

    /*
    ?与 from 函数
.Trait std::convert::From 上的 from 函数: 
    - 用于错误之间的转换
·被?所应用的错误，会隐式的被from函数处理·当?调用 from函数时:
    - 它所接收的错误类型会被转化为当前函数返回类型所定义的错误类型
·用于:针对不同错误原因，返回同一种错误类型
    - 只要每个错误类型实现了转换为所返回的错误类型的from 函数
 */ 
    fn read_file1() -> Result<String, io::Error> {
        let f = File::open("Cargo.toml");
        // EA -> EB, EA from -> EB
        let mut f = match f {
            Ok(get_file) => get_file,
            Err(err) => return Err(err),
        };
        let mut s = String::new();
        match f.read_to_string(&mut s) {
            Ok(_) => Ok(s),
            Err(err) => Err(err),
        }
    }

    /*
    ?链式调用
    ?运算符只能用于返回Result函数
     */
    #[test]
    fn err09() {
        let res = read_file3();
        match res {
            Ok(s) => println!("file: {}", s),
            Err(e) => println!("file err: {}", e),
        }
    }

    fn read_file3() -> Result<String, io::Error> {
        let mut s = String::new();
        let mut f = File::open("Cargo.toml")?.read_to_string(&mut s)?;
        Ok(s)
    }

    /*
    ?运算符与 main 函数
·main 函数返回类型是:() - (例子)
·main 函数的返回类型也可以是:Result<T,E> - (例子)
·Box<dyn Error> 是trait 对象:
    - 简单理解:“任何可能的错误类型”
 */
    fn main() -> Result<(), Box<dyn Error>> {
        let f = File::open("Cargo.toml");
        Ok(())
    }

    /* 
    总体原则: 
    在定义一个可能失败的函数时，优先考虑返回Result否则就panic! 

    编写示例、原型代码、测试·可以使用panic!
        - 演示某些概念:unwrap
        - 原型代码:unwrap、expect 
        - 测试:unwrap、expect

    有时你比编译器掌握更多的信息
    ·你可以确定 Result 就是 Ok: unwrap

    错误处理的指导性建议
    ·当代码最终可能处于损坏状态时，最好使用panic!
    ·损坏状态(Bad state):某些假设、保证、约定或不可变性被打破 
        -例如非法的值、矛盾的值或空缺的值被传入代码 
        - 以及下列中的一条:
        ·这种损坏状态并不是预期能够偶尔发生的事情。
        ·在此之后，您的代码如果处于这种损坏状态就无法运行。
        ·在您使用的类型中没有一个好的方法来将这些信息(处于损坏状态)进行编码。

    场景建议
    ·调用你的代码，传入无意义的参数值:panic!
    ·调用外部不可控代码，返回非法状态，你无法修复:panic!
    ·如果失败是可预期的:Result
    ·当你的代码对值进行操作，首先应该验证这些值:panic!
    */

}